/**
 * 一些对相关数据结构进行debug所用到的工具。
 */

#ifndef _AHAMOLETT_CDS_TEST_TOOL_H_
#define _AHAMOLETT_CDS_TEST_TOOL_H_
#ifdef __cplusplus
extern "C" {
#endif

#include <stdio.h>
#include <stdlib.h>
#include <cds/cds_types.h>
#include <cds/list.h>
#include <cds/bittool.h>
#include <cds/bitmap.h>
#include <cds/cfifo.h>
#include <cds/string.h>

/// 打印失败的测试用例
#define LOG_TEST_FAIL(fmt,...)  \
    printf("\033[1;31;40m[-FAIL-]\033[0m "  \
        "\033[1;35;40m%s\033[0m: "  \
        "\033[1;36;40m%s()\033[0m[%d]: "fmt,   \
        __FILE__, __func__, __LINE__, ##__VA_ARGS__)

/// 打印成功的测试用例
#ifndef TEST_FAIL_ONLY
#define LOG_TEST_PASS(fmt,...)  \
    printf("\033[1;32;40m[-PASS-]\033[0m "  \
        "\033[1;35;40m%s\033[0m: "  \
        "\033[1;36;40m%s()\033[0m[%d]: "fmt,   \
        __FILE__, __func__, __LINE__, ##__VA_ARGS__)
#endif

/// 交换两个同类型的变量
#define EXCHANGE_VALS(num1, num2)   do {    \
    typeof(num1) __tmp = (num1);    \
    (num1) = (num2);    \
    (num2) = __tmp; \
} while (0)


/********************************* bittools.h *********************************/



/*********************************** list.h ***********************************/
/// 用于测试list的结构体。
struct list_type {
    struct list_head    list_head;
    int key;
};

/// 测试单个链表节点的有效性。
static inline int
LIST_NODE_VALIDATION(struct list_head *node)
{
    if (node == NULL) {
        return -1;
    }

    if (node->next == NULL || node->prev == NULL) {
        return -1;
    }
    
    if (node->next->prev != node || node->prev->next != node) {
        return -1;
    }

    return 0;
}

static inline struct list_head*
LIST_FROM_ARRAY(const int *arr, int len)
{
    struct list_head *head = malloc(sizeof(struct list_head));
    list_head_init(head);

    for (int i = 0; i < len; i++) {
        struct list_type *new_node = malloc(sizeof(struct list_type));
        list_head_init(&new_node->list_head);
        new_node->key = arr[i];
        list_add_tail(head, &new_node->list_head);
    }

    return head;
}

/// 打印整个链表
static inline void
DISPLAY_WHOLE_LIST(struct list_head *head)
{
    int cnt = 0;
    struct list_head *pos = NULL;
    printf("[");
    list_foreach(head, pos) {
        if (LIST_NODE_VALIDATION(pos) != 0) {
            printf(" error!");
            break;
        }
        int pos_val = list_entry(pos, struct list_type, list_head)->key;
        printf(" %d,", pos_val);
        cnt++;
    }
    printf("] len: %d\n", cnt);
}

/// 测试一个链表内所有节点的合法性。都合法的话返回链表长度，否则返回-1
static inline int
VALIDATE_WHOLE_LIST(struct list_head *head)
{
    int cnt = 0;
    struct list_head *pos = NULL;

    if (LIST_NODE_VALIDATION(head) != 0) {
        return -1;
    }
    list_foreach(head, pos) {
        if (LIST_NODE_VALIDATION(pos) != 0) {
            return -1;
        }
        cnt++;
    }

    return cnt;
}

/// 对比链表的值是否匹配一个数组的值，如果全部匹配且中途没有出错返回匹配的个数，否则返回-1
static inline int
COMPARE_LIST_ARRAY(
    struct list_head *head, 
    const int *arr
)
{
    int cnt = 0;
    struct list_head *pos = NULL;

    if (LIST_NODE_VALIDATION(head) != 0) {
        return -1;
    }

    list_foreach(head, pos) {
        if (LIST_NODE_VALIDATION(pos) != 0) {
            return -1;
        }
        if (list_entry(pos, struct list_type, list_head)->key != arr[cnt]) {
            return -1;
        }
        cnt++;
    }

    return cnt;
}

/// 释放整个链表
#define DESTROY_WHOLE_LIST(head, type, member)  do {    \
    type *__pos = NULL; \
    type *__n = NULL;   \
    list_foreach_entry_safe(head, __pos, __n, list_head) {  \
        free(__pos);  \
    }   \
    free(head); \
    head = NULL;    \
} while (0)


/********************************** cfifo.h ***********************************/
/// 在缓冲区边界外额外保留的大小，为做越界测试。
#define TEST_CFIFO_PADDING_SIZE 16

/// 创建一个带PADDING的环形缓冲区。
static struct cfifo *CREATE_CFIFO_WITH_PADDING(size_t size)
{
    size_t buffer_size = size + (TEST_CFIFO_PADDING_SIZE * 3);
    void *buffer = malloc(buffer_size);
    if (buffer == NULL) {
        return NULL;
    }
    struct cfifo *fifo = malloc(sizeof(struct cfifo));
    if (fifo == NULL) {
        free(buffer);
        return NULL;
    }
    if (cfifo_init(fifo, buffer + TEST_CFIFO_PADDING_SIZE, size) == NULL)
    {
        free(buffer);
        free(fifo);
        return NULL;
    }
    memset(buffer, 0, buffer_size);
    return fifo;
}

/// 释放一个带PADDING的环形缓冲区。
static void DESTROY_CFIFO_WITH_PADDING(struct cfifo *fifo)
{
    if (fifo != NULL) {
        if (fifo->buffer != NULL) {
            free(fifo->buffer - TEST_CFIFO_PADDING_SIZE);
        }
        free(fifo);
    }
}

#define CFIFO_TEST_BACKWARD_OUTRANGE    1   /// 写buffer向下越界
#define CFIFO_TEST_FORWARD_OUTRANGE     2   /// 写buffer向上越界
#define CFIFO_TEST_READ_PTR_ERR         4   /// cfifo读指针越界
#define CFIFO_TEST_WRITE_PTR_ERR        8   /// cfifo写指针越界
#define CFIFO_TEST_PTR_INBALANCE        16  /// cfifo内的属性不平衡
#define CFIFO_TEST_NULL_BUFFER          32  /// cfifo的BUFFER为空
#define CFIFO_TEST_BUFFER_COUNT_ERR     64  /// cfifo的缓冲计数太大

/// cfifo缓冲的越界测试，没有越界返回0，有越界在相应的位置一并返回。
static int CFIFO_OUTOF_RANGE_TEST(struct cfifo *fifo)
{
    int ret = 0;
    char *buffer = fifo->buffer;
    if (buffer == NULL) {
        return CFIFO_TEST_NULL_BUFFER;
    } 
    buffer -= TEST_CFIFO_PADDING_SIZE;
    for (int i = 0; i < TEST_CFIFO_PADDING_SIZE; i++) {
        if (buffer[i] != 0) {
            ret |= CFIFO_TEST_BACKWARD_OUTRANGE;
            break;
        }
    }
    buffer = fifo->buffer + fifo->mask + 1;
    for (int i = 0; i < TEST_CFIFO_PADDING_SIZE; i++) {
        if (buffer[i] != 0) {
            ret |= CFIFO_TEST_FORWARD_OUTRANGE;
            break;
        }
    }
    return ret;
}

/// cfifo读写指针的合法性测试
static int CFIFO_RW_PTR_TEST(struct cfifo *fifo)
{
    int ret = 0;
    if ((fifo->read_ptr & fifo->mask) != fifo->read_ptr) {
        ret |= CFIFO_TEST_READ_PTR_ERR;
    }
    if ((fifo->write_ptr & fifo->mask) != fifo->write_ptr) {
        ret |= CFIFO_TEST_WRITE_PTR_ERR;
    }
    int ptr_difference =    \
        (fifo->write_ptr & fifo->mask) - (fifo->read_ptr & fifo->mask);
    if (ptr_difference < 0) {
        ptr_difference += fifo->mask + 1;
    }
    if (ptr_difference != 0) {
        if (ptr_difference != fifo->content_size) {
            ret |= CFIFO_TEST_PTR_INBALANCE;
        }
    } else {
        if(fifo->content_size != (fifo->mask + 1) && fifo->content_size != 0) {
            ret |= CFIFO_TEST_PTR_INBALANCE;
        }
    }
    if (fifo->content_size > (fifo->mask + 1)) {
        ret |= CFIFO_TEST_BUFFER_COUNT_ERR;
    }
    return ret;
}

/// 对cfifo进行所有的测试
static int CFIFO_FULLY_TEST(struct cfifo *fifo)
{
    int ret = 0;
    ret |= CFIFO_OUTOF_RANGE_TEST(fifo);
    ret |= CFIFO_RW_PTR_TEST(fifo);
    return ret;
}

#define SHOW_CFIFO_ERROR_INFO(err)  do {    \
    if (err == 0) { \
        printf("no error!\n");  \
        break;  \
    }   \
    if (err & CFIFO_TEST_BACKWARD_OUTRANGE) {   \
        printf("(0) cfifo out of range before buffer!\n");  \
    }   \
    if (err & CFIFO_TEST_FORWARD_OUTRANGE) {    \
        printf("(1) cfifo out of range after buffer!\n");   \
    }   \
    if (err & CFIFO_TEST_READ_PTR_ERR) {    \
        printf("(2) cfifo's read ptr is out of range!\n");  \
    }   \
    if (err & CFIFO_TEST_WRITE_PTR_ERR) {   \
        printf("(3) cfifo's write ptr is out of range!\n"); \
    }   \
    if (err & CFIFO_TEST_PTR_INBALANCE) {   \
        printf("(4) cfifo's ptrs cannot fix on its buffer count!\n");   \
    }   \
    if (err & CFIFO_TEST_NULL_BUFFER) { \
        printf("(5) cfifo with a NULL buffer!\n");  \
    }   \
    if (err & CFIFO_TEST_BUFFER_COUNT_ERR) {    \
        printf("(6) cfifo's buffer count is out of range!\n");  \
    }   \
} while (0)


/********************************* bitmap.h ***********************************/
/// 在缓冲区边界外额外保留的大小，为做越界测试。
#define TEST_BITMAP_PADDING_SIZE    16

/// 创建一个带PADDING的位图
static struct bitmap *CREATE_BITMAP_WITH_PADDING(size_t size)
{
    size_t buffer_size = size + TEST_BITMAP_PADDING_SIZE * 3;
    struct bitmap *bm = malloc(sizeof(struct bitmap));
    if (bm == NULL) {
        return NULL;
    }
    void *buffer = malloc(buffer_size);
    if (buffer == NULL) {
        free(bm);
        return NULL;
    }
    memset(buffer, 0x55, buffer_size);

    if (bitmap_init(bm, buffer + TEST_BITMAP_PADDING_SIZE, size) == NULL)
    {
        free(bm);
        free(buffer);
        return NULL;
    }
    return bm;
}

/// 复制一个带PADDING的位图
static struct bitmap *CLONE_BITMAP_WITH_PADDING(struct bitmap *old_bm)
{
    if (!bitmap_is_initialized(old_bm)) {
        return NULL;
    }
    size_t buffer_size = old_bm->map_size + TEST_BITMAP_PADDING_SIZE * 3;
    struct bitmap *bm = malloc(sizeof(struct bitmap));
    if (bm == NULL) {
        return NULL;
    }
    void *buffer = malloc(buffer_size);
    if (buffer == NULL) {
        free(bm);
        return NULL;
    }
    memset(buffer, 0x55, buffer_size);

    if (bitmap_copy(bm, old_bm, buffer + TEST_BITMAP_PADDING_SIZE) == NULL)
    {
        free(bm);
        free(buffer);
        return NULL;
    }
    return bm;
}

/// 释放一个带PADDING的位图
static void DESTROY_BITMAP_WITH_PADDING(struct bitmap *bm)
{
    if (bm != NULL) {
        if (bm->buffer != NULL) {
            free(bm->buffer - TEST_BITMAP_PADDING_SIZE);
        }
        free(bm);
    }
}

#define BITMAP_TEST_BACKWARD_OUTRANGE   1
#define BITMAP_TEST_FORWARD_OUTRANGE    2
#define BITMAP_TEST_NULL_BUFFER         4
#define BITMAP_TEST_UNINITIALIZED       8
#define BITMAP_TEST_ZERO_SIZE           16
/// 位图有效性测试。
static int BITMAP_FULLY_TEST(struct bitmap *bm)
{
    int ret = 0;
    if (!bitmap_is_initialized(bm)) {
        ret |= BITMAP_TEST_UNINITIALIZED;
        if (bm != NULL) {
            if (bm->buffer == NULL) {
                ret |= BITMAP_TEST_NULL_BUFFER;
            }
            if (bm->map_size == 0) {
                ret |= BITMAP_TEST_ZERO_SIZE;
            }
        }
    }
    unsigned char *buffer =     \
        (unsigned char*)(bm->buffer - TEST_BITMAP_PADDING_SIZE);
    for (int i = 0; i < TEST_BITMAP_PADDING_SIZE; i++) {
        if (buffer[i] != 0x55) {
            ret |= BITMAP_TEST_BACKWARD_OUTRANGE;
            break;
        }
    }
    buffer =  (unsigned char*)(bm->buffer + bm->map_size);
    for (int i = 0; i < TEST_BITMAP_PADDING_SIZE; i++) {
        if (buffer[i] != 0x55) {
            ret |= BITMAP_TEST_FORWARD_OUTRANGE;
            break;
        }
    }

    return ret;
}

/// 对比两个位图是否一致，如果一致返回位图的长度，否则返回第一个不一致的字节索引。
static unsigned long BITMAP_EQUAL_TEST(struct bitmap *bm1, struct bitmap *bm2)
{
    if (!bitmap_is_initialized(bm1) || !bitmap_is_initialized(bm2)) {
        return 0;
    }
    size_t cnt = 0;
    size_t size = bm1->map_size > bm2->map_size ? bm2->map_size : bm1->map_size;

    while (cnt < size) {
        if (bm1->buffer[cnt] != bm2->buffer[cnt]) {
            break;
        }
        cnt++;
    }

    return cnt;
}

/// 测试位图是否为空。
static int BITMAP_EMPTY_TEST(struct bitmap *bm)
{
    if (bitmap_is_initialized(bm)) {
        for (int i = 0; i < bm->map_size; i++) {
            if (bm->buffer[i] != 0) {
                return 0;
            }
        }
    }
    return 1;
}

/// 测试位图是否全被置位。
static int BITMAP_ALL_SET_TEST(struct bitmap *bm)
{
    if (bitmap_is_initialized(bm)) {
        for (int i = 0; i < bm->map_size; i++) {
            if (bm->buffer[i] != 0xff) {
                return 0;
            }
        }
    }
    return 1;
}

#define SHOW_BITMAP_ERROR_INFO(err) do {    \
    if ((err) == 0) {   \
        printf("no error!\n");  \
    }   \
    if ((err) & BITMAP_TEST_BACKWARD_OUTRANGE) {    \
        printf("(0) bitmap out of range before buffer!\n"); \
    }   \
    if ((err) & BITMAP_TEST_FORWARD_OUTRANGE) { \
        printf("(1) bitmap out of range after buffer!\n");  \
    }   \
    if ((err) & BITMAP_TEST_NULL_BUFFER) {  \
        printf("(2) bitmap with a NULL buffer!\n"); \
    }   \
    if ((err) & BITMAP_TEST_UNINITIALIZED) {    \
        printf("(3) uninitialized bitmap!\n");  \
    }   \
    if ((err) & BITMAP_TEST_ZERO_SIZE) {    \
        printf("(4) size of bitmap is zero!\n");    \
    }   \
} while (0)


#ifdef __cplusplus
}
#endif
#endif
